

#include "../luaState.h"
#include <iostream>
#include <boost/filesystem.hpp>
#include <boost/make_shared.hpp>
#include <log/log.h>
#include <appendix/phy_constants.h>
#ifndef __cplusplus
#include "stdlib.h"
#endif
#include "string.h"

#include <boost/signals2/signal.hpp>
#include <boost/bind.hpp>
#include <boost/regex.hpp>
#include <thread>
#include <boost/thread/mutex.hpp>
#include <iostream>

extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}




namespace lua{

		static int luaMy_print (lua_State *L) {
		  int n = lua_gettop(L);  /* number of arguments */
		  int i;
		  lua_getglobal(L, "tostring");
		  std::ostringstream out;
		  for (i=1; i<=n; i++) {
			const char *s;
			lua_pushvalue(L, -1);  /* function to be called */
			lua_pushvalue(L, i);   /* value to print */
			lua_call(L, 1, 1);
			s = lua_tostring(L, -1);  /* get result */
			if (s == NULL)
			  return luaL_error(L, LUA_QL("tostring") " must return a string to "
								   LUA_QL("print"));
			if (i>1)  out << "\t";
		//		fputs("\t", stdout);
		 //   fputs(s, stdout);
			out << s ;
			lua_pop(L, 1);  /* pop result */
		  }
		  //fputs("\n", stdout);
		//  out << "\n";
		  logs::info_log( out.str().c_str() );
		  return 0;
		}


		static void stackDump(lua_State* L){
		    std::cout<< "\nbegin dump lua stack"<< std::endl;
		    int i = 0;
		    int top = lua_gettop(L);
		    for (i = 1; i <= top; ++i) {
		        int t = lua_type(L, i);
		        switch (t) {
		            case LUA_TSTRING:
		            {
		                printf("%d: string,    '%s' \n", i, lua_tostring(L, i));
		            }
		                break;
		            case LUA_TBOOLEAN:
		            {
		                printf(lua_toboolean(L, i) ? "true " : "false ");
		            }break;
		            case LUA_TNUMBER:
		            {
		                printf("%d: NUMBER,    %g \n", i, lua_tonumber(L, i));
		            }
		                break;
		            default:
		            {
		                printf("%d: %s \n", i, lua_typename(L, t));
		            }
		                break;
		        }
		    }
		    std::cout<<"\nend dump lua stack" << std::endl;
		}


		//////////////////////////////////////////////////////////
		//调用Lua语言的 debug.traceback
		static int traceback (lua_State *L) {
		  if (!lua_isstring(L, 1))  /* 'message' not a string? */
		    return 1;  /* keep it intact */
		  lua_getfield(L, LUA_RIDX_GLOBALS, "debug");
		  if (!lua_istable(L, -1)) {
		    lua_pop(L, 1);
		    return 1;
		  }
		  lua_getfield(L, -1, "traceback");
		  if (!lua_isfunction(L, -1)) {
		    lua_pop(L, 2);
		    return 1;
		  }
		  lua_pushvalue(L, 1);  /* pass error message */
		  lua_pushinteger(L, 2);  /* skip this function and traceback */
		  lua_call(L, 2, 1);  /* call debug.traceback */
		  return 1;
		}
		//////////////////////////////////////////////////////////

		//////////////////////////////////////////////////////////
		// lua_gettop: 栈的元素数量
		static int docall (lua_State *L, int narg, int clear) {
		  int status;
		  int base = lua_gettop(L) - narg;  /* function index */
		  lua_pushcfunction(L, traceback);  /* push traceback function */
		  lua_insert(L, base);  /* put it under chunk and args */
		  //stackDump(L);
		 // signal(SIGINT, laction);
		  status = lua_pcall(L, narg, (clear ? 0 : LUA_MULTRET), base);
		  //signal(SIGINT, SIG_DFL);
		  lua_remove(L, base);  /* remove traceback function */
		  /* force a complete garbage collection in case of errors */
		  if (status != 0) lua_gc(L, LUA_GCCOLLECT, 0);
		  return status;
		}
		//////////////////////////////////////////////////////////



		static int lua_GetStopFlag (lua_State *L) {
			lua_getglobal(L, "ThisLuaState");
			luaState** console  = (luaState**)lua_touserdata(L,-1);
			if( *console )
			{
				bool flag = (*console)->stopFlag();
				lua_pushboolean(L, flag);   /* value to print */
			    //lua_call(L, 0, 1);
			    return 1;
			}else{
				logs::error_log("Get this state error");
			}
			return 0;
		}

		static int lua_GetStopSolverFlag (lua_State *L) {
			lua_getglobal(L, "ThisLuaState");
			luaState** console  = (luaState**)lua_touserdata(L,-1);
			if( *console )
			{
				bool flag = (*console)->stopSolverFlag();
				lua_pushboolean(L, flag);   /* value to print */
			    //lua_call(L, 0, 1);
			    return 1;
			}else{
				logs::error_log("Get this state error");
			}
			return 0;
		}

		static int lua_GetSuspendFlag (lua_State *L) {
			lua_getglobal(L, "ThisLuaState");
			luaState** console  = (luaState**)lua_touserdata(L,-1);
			if( *console )
			{
				bool flag = (*console)->suspendFlag();
				lua_pushboolean(L, flag);   /* value to print */
			    //lua_call(L, 0, 1);
			    return 1;
			}else{
				logs::error_log("Get this state error");
			}
			return 0;
		}

		/*
		static void set_suspend_flag(luaState** console, int set )
		{
			(*console)->setSuspendFlag(set);
		}
		*/


		luaState::luaState()
		:_err(0),_autoClose(1)
		,_stopFlag(0),_suspendFlag(0),_stopSolverFlag(0)
		,m_mutex( std::make_shared<std::mutex>() )
		,m_flagMutex( std::make_shared<std::mutex>() )
		{
			_state = luaL_newstate();
			if(_state==NULL)
			{
				logs::error_log("luaState cannot create state: not enough memory");
				return;
			}
			openLibs();
			_paths.push_back( "" );
			_cpaths.push_back( "" );
		}

		luaState::luaState(lua_State* s)
		:_err(0),_state(s),_autoClose(0)
		,_stopFlag(0),_suspendFlag(0),_stopSolverFlag(0)
		,m_mutex( std::make_shared<std::mutex>() )
		,m_flagMutex( std::make_shared<std::mutex>() )
		{
			//registerGlobalFunction();
		}

		void luaState::setState(lua_State* s)
		{
			if(_autoClose)
				lua_close(_state);
			_autoClose = 0;
			_state = s;
			//registerGlobalFunction();
		}

		lua_State* luaState::state()
		{
			if(_state==NULL)
			{
				logs::error_log("luaState state invalid");
			}
			//logs::debug_log("luaState get state");
			return _state;
		}

		luaState::~luaState()
		{
			if(_autoClose)
				lua_close(_state);
		}
		void luaState::openLibs()
		{
			luaL_openlibs(_state);
			registerGlobalFunction();

		}
		int luaState::openLib(boost::function< int(lua_State*) > fun)
		{
			return fun(_state);
		}

		void luaState::registerGlobalFunction(){
			lua_register(_state,"print",luaMy_print);
			lua_register(_state,"GetStopFlag",lua_GetStopFlag);
			lua_register(_state,"GetStopSolverFlag",lua_GetStopSolverFlag);
			lua_register(_state,"GetSuspendFlag",lua_GetSuspendFlag);
			luaState** thisState = (luaState**) lua_newuserdata(_state,sizeof(luaState*));
			*thisState = this;
			lua_setglobal(_state, "ThisLuaState");   //设置lua全局变量getTwoVar
		}

		int luaState::stopFlag(){
			std::scoped_lock lock(*m_flagMutex);
			return _stopFlag;
		}

		int luaState::stopSolverFlag(){
			std::scoped_lock lock(*m_flagMutex);
			return _stopSolverFlag;
		}

		int luaState::suspendFlag(){
			std::scoped_lock lock(*m_flagMutex);
			return _suspendFlag;
		}

		void luaState::setStopFlag(int set){
			std::scoped_lock lock(*m_flagMutex);
			_stopFlag = set;
		}

		void luaState::setStopSolverFlag(int set){
			std::scoped_lock lock(*m_flagMutex);
			_stopSolverFlag = set;
		}

		void luaState::setSuspendFlag(int set){
			std::scoped_lock lock(*m_flagMutex);
			_suspendFlag = set;
		}

		void luaState::setPath(const char* path){
			_filePath = path;
			initEnv();
		}

		void luaState::initEnv()
		{
			boost::filesystem::current_path( _filePath.parent_path() );
			std::string parent_path_str = _filePath.parent_path().generic_string();
			const char* cpathvar = getenv("LUA_CPATH");
			const char* pathvar = getenv("LUA_PATH");
			std::string l_package_path = "package.path = package.path ..";
			l_package_path += "\";";
		#ifdef WIN32
			l_package_path += "c:/lua/?.lua;" ;
		#endif
			l_package_path += parent_path_str;
			l_package_path += "/?.lua;";
			//l_package_path += path_env.generic_string().c_str();
			if(pathvar)l_package_path += pathvar;
			l_package_path += "\"";
			//logs::debug_log( "load script l_package_path:  " , l_package_path );
			//std::cout << "l_package_path:  " << l_package_path << std::endl;

			std::string l_package_cpath = "package.cpath = package.cpath ..";
			l_package_cpath += "\";";
			l_package_cpath += parent_path_str;
			l_package_cpath += "/?.dll;";
			l_package_cpath += parent_path_str;
			l_package_cpath += "/?.so;";
			if(cpathvar)l_package_cpath += cpathvar;
			l_package_cpath += ";";
			l_package_cpath += "\"";

			doString(l_package_path);
		}

		void luaState::initPath()
		{
			std::string l_package_path = "package.path = \"";
			const char* pathvar = getenv("LUA_PATH");
			if(pathvar)
			{
			    std::string res = boost::regex_replace(std::string(pathvar),boost::regex("\\\\\\\\"),"/");
			    res = boost::regex_replace(res,boost::regex("\\\\"),"/");
				l_package_path += res.c_str();
				l_package_path += ";";
			}
			for( auto path:_paths)
			{
				l_package_path += path;
				l_package_path += "/?.lua;";
			}
			l_package_path += "\"";
			//logs::debug_log("Lua PATH: %s",l_package_path.c_str());
			doString(l_package_path);

		}

		void luaState::initCPath()
		{
			std::string l_package_cpath = "package.cpath = \"";
			const char* pathvar = getenv("LUA_CPATH");
			if(pathvar)
			{
			    std::string res = boost::regex_replace(std::string(pathvar),boost::regex("\\\\\\\\"),"/");
			    res = boost::regex_replace(res,boost::regex("\\\\"),"/");
			    l_package_cpath += res.c_str();
			    l_package_cpath += ";";
			}
			for( auto path:_cpaths)
			{
				//l_package_cpath += "\";";
				l_package_cpath += path;
				l_package_cpath += "/?.dll;";
				l_package_cpath += path;
				l_package_cpath += "/?.so;";
			}
			l_package_cpath += "\"";

			//logs::debug_log("Lua CPATH: %s",l_package_cpath.c_str());
			doString(l_package_cpath);

		}

		void luaState::addPath( const char* str )
		{
			boost::filesystem::path path( str );
			if( !boost::filesystem::is_directory(path) )
			{
				logs::error_log(boost::format("The directory does not exist: %1%") % str);
				return;
			}
			path = boost::filesystem::complete(path);
			_paths.push_back( path.generic_string() );
		}

		void luaState::addCPath( const char* str )
		{
			boost::filesystem::path path( str );
			if( !boost::filesystem::is_directory(path) )
			{
				logs::error_log(boost::format("The directory is not exist: %1%") % str);
				return;
			}
			path = boost::filesystem::complete(path);
			_cpaths.push_back( path.generic_string() );
		}
		void luaState::addTempPath( const char* str )
		{
			boost::filesystem::path path( str );
			if( !boost::filesystem::is_directory(path) )
			{
				logs::error_log(boost::format("The directory is not exist: %1%") % str);
				return;
			}
			path = boost::filesystem::complete(path);

			std::string l_package_path = "package.path = package.path ..";
			l_package_path += "\";";
			l_package_path += path.generic_string();
			l_package_path += "/?.lua;";
			l_package_path += "\"";

			std::string l_package_cpath = "package.cpath = package.cpath ..";
			l_package_cpath += "\";";
			l_package_cpath += path.generic_string();
			l_package_cpath += "/?.dll;";
			l_package_cpath += path.generic_string();
			l_package_cpath += "/?.so;";
			l_package_cpath += "\"";

			doString(l_package_cpath);
			doString(l_package_path);
		}



		int luaState::doFile(const char* fName)
		{
			return doFileImpl(fName);
		}

		int luaState::doFileImpl(const char* fName)
		{
			_err =luaL_dofile(_state,fName) ;
			errorHandle();
			return _err;
		}

		int luaState::doString(const char* str)
		{
			return doStringImpl(str);
		}
		int luaState::callFun(const char* str)
		{
			return callFunImpl(str);
		}


		int luaState::doStringImpl(const char* str)
		{
			std::scoped_lock lock(*m_mutex);
			_err =luaL_dostring(_state,str) ;
			errorHandle();
			return _err;
		}

		int luaState::loadFile(const char* fName)
		{
			std::scoped_lock lock(*m_mutex);
			_err =luaL_loadfile(_state,fName) ;
			errorHandle();
			return _err;
		}

		int luaState::loadString(const char* fName)
		{
			std::scoped_lock lock(*m_mutex);
			_err = luaL_loadstring(_state, fName);
			errorHandle();
			return _err;
		}
		int luaState::doCall( int narg, int clear)
		{
			std::scoped_lock lock(*m_mutex);
			_err =docall(_state,narg,clear);
			errorHandle();
			return _err;
		}
		void luaState::errorHandle()
		{
			if(_err)
			{
				_errStr =  lua_tostring(_state, -1);
				lua_pop(_state, 1);
				if( !stopFlag() )
					logs::error_log( _errStr.c_str() );
			}
		}

		int luaState::callFunImpl( const char* name )
		{
			return _err;
		}

};


